package cn.likun.kit.io.template.support;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.UUID;

import javax.sql.DataSource;

import org.apache.commons.collections4.MultiMap;
import org.apache.commons.collections4.map.MultiValueMap;
import org.apache.tools.ant.filters.StringInputStream;
import org.apache.tools.zip.ZipEntry;
import org.apache.tools.zip.ZipFile;
import org.apache.tools.zip.ZipOutputStream;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;

import cn.likun.kit.database.MetaDataCommons;
import cn.likun.kit.database.dialect.DialectCommons;
import cn.likun.kit.file.FileCommons;
import cn.likun.kit.stream.StreamCommons;
import cn.likun.kit.validate.ValidateCommons;

/**
 * template IO 工具类接口
 */
public abstract class BaseIOSupport {
	
	public static final String GENERATOR_TYPE_IDENTITY = "GT_IDENTITY";

	protected int batchSize = 10000;
	protected boolean accuratePositioning = true;
	protected InputStream templateIn;
	protected String charset = "UTF-8";
	protected NamedParameterJdbcTemplate jdbcTemplate;
	protected MetaDataCommons metaDataCommons;
	protected DialectCommons dialectCommons;
	protected Map<String,String> args = new HashMap<String,String>();
	
	private MultiMap insertMap = new MultiValueMap();
	private StringBuffer outResult = new StringBuffer(batchSize);
	/**
	 * 构造函数 , 基于模板文件路径的 IOSupport 
	 * @param templatePath 模板文件路径
	 * @param dataSource 数据源
	 */
	public BaseIOSupport(String templatePath, DataSource dataSource) throws ClassNotFoundException, FileNotFoundException, SQLException {
		init(new FileInputStream(templatePath), dataSource);
	}
	/**
	 * 构造函数 , 基于模板字符串的 IOSupport 构造函数
	 * @param templateString 模板字符串
	 * @param encoding 字符串编码 , 可以为空
	 * @param dataSource 数据域
	 */
	public BaseIOSupport(String templateString, String encoding, DataSource dataSource) throws ClassNotFoundException, SQLException {
		InputStream templateIn = null;
		if (ValidateCommons.isEmpty(encoding)) {
			templateIn = new StringInputStream(templateString);
		} else {
			templateIn = new StringInputStream(templateString,encoding);
		}
		init(templateIn, dataSource);
	}
	/**
	 * 构造函数 , 基于模板Byte 数组的 IOSupport 构造函数
	 * @param templateBytes 模板 byte 数组
	 * @param dataSource 数据源
	 */
	public BaseIOSupport(byte[] templateBytes, DataSource dataSource) throws ClassNotFoundException, SQLException {
		init(new ByteArrayInputStream(templateBytes), dataSource);
	}
	/**
	 * 构造函数 基于模板输入流的 IOSupport 
	 * @param templateIn 模板输入流
	 * @param dataSource 数据源
	 */
	public BaseIOSupport(InputStream templateIn, DataSource dataSource) throws ClassNotFoundException, SQLException {
		init(templateIn, dataSource);
	}
	/**
	 * 初始化工具类
	 * @param templateIn 模板输入流
	 * @param dataSource 数据源
	 */
	private void init(InputStream templateIn, DataSource dataSource) throws ClassNotFoundException, SQLException {
		this.jdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
		this.metaDataCommons = new MetaDataCommons(dataSource);
		this.dialectCommons = DialectCommons.getInstance(this.metaDataCommons.getDbType());
		this.templateIn = templateIn;
	}
	/**
	 * 设置 IO 批次大小 , 默认 10000 
	 */
	public void setBatchSize(int batchSize) {
		this.batchSize = batchSize;
	}
	
	public String getCharset() {
		return charset;
	}
	/**
	 * 数据导出的时候   zip 设置编码
	 * @param charset
	 */
	public void setCharset(String charset) {
		this.charset = charset;
	}
	/**
	 * &nbsp;&nbsp;&nbsp;&nbsp;是否开启 insert 精准定位模式 , 开启后,当批量执行出现异常则会进入逐条执行模式,<br>
	 * 此模式下可以定位到每条SQL 是否异常 , 但是效率低下,默认值为true
	 * @param accuratePositioning 默认为true
	 */
	public void openAccuratePositioning(boolean accuratePositioning) {
		this.accuratePositioning = accuratePositioning;
	}
	/**
	 * 设置自定义参数
	 * @param key 
	 * @param value
	 */
	public void putArg(String key, String value) {
		this.args.put(key, value);
	}
	/**
	 * 设置自定义参数
	 * @param args
	 */
	public void putArgs(Map<String,String> args) {
		this.args.putAll(args);
	}
	/**
	 * 模板的文件类型
	 */
	public enum TemplateFileType {
		TYPE_XML,
		TYPE_EXCEL_XLS,
		TYPE_EXCEL_XLSX,
		TYPE_TEXT_TXT,
		TYPE_TEXT_OTHER
	}
	
	protected abstract TemplateFileType getTemplateFileType();
	
	/**
	 * 基于数据输入流的导入
	 * @param resourceIn
	 */
	public abstract void input(InputStream resourceIn) throws Exception;
	/**
	 * 基于 数据 bytes 的导入
	 * @param resourceBytes
	 */
	public void input(byte[] resourceBytes) throws Exception {
		input(new ByteArrayInputStream(resourceBytes));
	}
	/**
	 * 基于数据字符串的导入 
	 * @param resourceString 
	 * @param encoding 字符串编码 , 可以为空
	 */
	public void input(String resourceString, String encoding) throws Exception {
		InputStream resourceIn = null;
		if (ValidateCommons.isEmpty(encoding)) {
			resourceIn = new StringInputStream(resourceString);
		} else {
			resourceIn = new StringInputStream(resourceString, encoding);
		}
		input(resourceIn);
	}
	/**
	 * 基于数据文件路径的导入
	 * @param resourcePath
	 */
	public void input(String resourcePath) throws FileNotFoundException, Exception {
		input(new FileInputStream(resourcePath));
	}
	/**
	 * 将导出的数据生成文件并打包为zip
	 * @param folder 根目录  ( x:/xx/xxx/ ) , 可以为空 , 默认为java.io.tmpdir
	 * @param name zip文件名 ( xxx.zip ), 可以为空 , 默认为  'uuid.zip' 
	 * @return zip 文件路径
	 * @throws Exception 
	 */
	public String output(String folder, String name) throws Exception {
		if (ValidateCommons.isEmpty(folder)) {
			//默认放在临时目录下
			folder = new StringBuffer().append(System.getProperty("java.io.tmpdir")).append("IO_DIR").toString() ;
			folder = FileCommons.changeSeparator(folder);
			FileCommons.createFolder(folder);
		}
		if (ValidateCommons.isEmpty(name)) {
			name = new StringBuffer().append(UUID.randomUUID().toString()).append(".zip").toString() ;
		}
		String zipPath = new StringBuffer().append(folder).append(File.separator).append(name).toString();
		zipPath = FileCommons.changeSeparator(zipPath);
		ZipOutputStream zipOut = new ZipOutputStream(new File(zipPath));
		zipOut.setEncoding(charset);
		try {
			outputProcess(zipOut);
		} finally{
			if (zipOut != null) {
				zipOut.close();
			}
		}
		return zipPath;
	}
	/**
	 * 导出过程 , 实现数据构建 并打包到 zip 中
	 * @param zipOut zip输出流 , ZipEntry 实例名为随机字串 
	 * @throws Exception
	 */
	protected abstract void outputProcess(ZipOutputStream zipOut) throws Exception;
	/**
	 * 将数据从输出流的导出
	 * @param resourceOut 输出流
	 * @throws FileNotFoundException
	 * @throws IOException
	 * @throws Exception
	 */
	public void output(OutputStream resourceOut) throws FileNotFoundException, IOException, Exception {
		StreamCommons.binaryTransport(new FileInputStream(output(null, null)), resourceOut, true);
	}
	/**
	 * <p>将数据生成字符串导出</p>
	 * 警告: 此方法只支持文本类型文件
	 * @param charset 字符集编码 , 可以为空
	 * @return 字符串集合
	 * @throws Exception 
	 */
	public List<String> output(String charset) throws Exception {
		List<String> retList = new ArrayList<String>();
		String zipPath = output(null, null);
		ZipFile zipFile = new ZipFile(zipPath);
		Enumeration< ? extends ZipEntry> entries = zipFile.getEntries();
		while (entries.hasMoreElements()) {
			ZipEntry zipEntry = entries.nextElement();
			InputStream in = zipFile.getInputStream(zipEntry);
			if (ValidateCommons.isEmpty(charset)) {
				retList.add(StreamCommons.textReader(new InputStreamReader(in)));
			} else {
				retList.add(StreamCommons.textReader(new InputStreamReader(in,charset)));
			}
		}
		return retList ;
	}
	
	protected void doInsert(String sql, Map<String,String> param, boolean fullExecute) {
		try {
			//数据准备
			if (ValidateCommons.isNotEmpty(sql) && ValidateCommons.isNotEmpty(param, false, null)) {
				//同步所有共有参数
				param.putAll(args);
				//处理主键策略
				Iterator<Entry<String, String>> paramIterator = param.entrySet().iterator();
				while (paramIterator.hasNext()) {
					Entry<String,String> paramEntry = paramIterator.next();
					String key = paramEntry.getKey();
					String value = paramEntry.getValue();
					if (GENERATOR_TYPE_IDENTITY.equals(value)) {
						param.put(key, UUID.randomUUID().toString());
					}
				}
				insertMap.put(sql, param);
			}
			
			//数据导入
			if (fullExecute) {
				Iterator<Entry<String,List<Map<String,Object>>>> insertIterator = insertMap.entrySet().iterator();
				while (insertIterator.hasNext()) {
					Entry<String,List<Map<String,Object>>> insertEntry = insertIterator.next();
					String sqlKey = insertEntry.getKey();
					List<Map<String,Object>> sqlParams = insertEntry.getValue();
					jdbcTemplate.batchUpdate(sqlKey, sqlParams.toArray(new Map[]{}));
				}
				
			} else {
				
			}
			
		} catch (Exception e) {
			
		}
	}
	
	
	
}






























